package cn.taobaoke.restful.datasource;

import cn.taobaoke.restful.domain.DomainEntity;
import cn.taobaoke.restful.domain.DomainRepository;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CustomerDataSource {

    public void initCustomDataSources() {
        logger.info("Register Customer Datasource");
        DefaultListableBeanFactory registry = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();
        BeanDefinition bd = registry.getBeanDefinition("dataSource");
        DynamicDataSource source = (DynamicDataSource) registry.getBean("dataSource");
        MutablePropertyValues mutablePropertyValues = bd.getPropertyValues();

        Map<Object, Object> targetDataSources = (Map<Object, Object>) mutablePropertyValues.get("targetDataSources");

        List<DomainEntity> domainEntityList = domainRepository.findAll();
        for (DomainEntity entity : domainEntityList) {
            String dataSourceType = entity.getDatasourceType();
            ExecutorService service = Executors.newSingleThreadExecutor();

            Future<?> future = service.submit(() -> targetDataSources.put(dataSourceType, buildHikariDataSource(entity)));
            try {
                future.get(3000, TimeUnit.MILLISECONDS);
                DynamicDataSourceContextHolder.dataSourceMap.put(dataSourceType, entity.getDomain());
            } catch (Exception e) {
                future.cancel(true);
                service.shutdown();
                logger.error("数据库连接超时 [{}] - {}", entity.getDomain(), e);
            }

        }

        source.afterPropertiesSet();
    }

    private DataSource buildHikariDataSource(final DomainEntity domainEntity) {
        HikariConfig config = new HikariConfig();

        String driverClassName = environment.getProperty("spring.datasource.driver-class-name");
        Optional<Object> poolSizeOpt = Optional.ofNullable(environment.getProperty("spring.datasource.hikari.maximum-pool-size"));
        Optional<Object> minIdleOpt = Optional.ofNullable(environment.getProperty("spring.datasource.hikari.minimum-idle"));
        Optional<Object> maxLifetimeOpt = Optional.ofNullable(environment.getProperty("spring.datasource.hikari.max-lifetime"));
        int poolSize = Integer.parseInt(poolSizeOpt.orElse("10").toString());
        int minIdle = Integer.parseInt(minIdleOpt.orElse("10").toString());
        int maxLifetime = Integer.parseInt(maxLifetimeOpt.orElse("1800000").toString());

        config.setDriverClassName(driverClassName);
        config.setJdbcUrl(domainEntity.getJdbcUrl());
        config.setUsername(domainEntity.getJdbcUsername());
        config.setPassword(domainEntity.getJdbcPassword());
        config.setMaximumPoolSize(poolSize);
        config.setMinimumIdle(minIdle);
        config.setMaxLifetime(maxLifetime);
        config.setPoolName("HikariPool-" + domainEntity.getDatasourceType());

        return new HikariDataSource(config);
    }

    private final ApplicationContext context;

    private final Environment environment;

    private final @NonNull
    DomainRepository domainRepository;

    private static Logger logger = LoggerFactory.getLogger(CustomerDataSource.class);
}
